package com.xuecheng.media.service.impl;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoRequest;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.media.model.qo.QueryAuditMediaModel;
import com.xuecheng.api.media.model.qo.QueryMediaModel;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.AuditEnum;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.common.ResourceTypeEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.media.common.constant.MediaErrorCode;
import com.xuecheng.media.common.utils.AliyunVODUtil;
import com.xuecheng.media.controller.MediaAuditController;
import com.xuecheng.media.convert.EntityMapper;
import com.xuecheng.media.entity.Media;
import com.xuecheng.media.entity.MediaDTO;
import com.xuecheng.media.mapper.MediaMapper;
import com.xuecheng.media.service.MediaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

/**
 * <p>
 * 媒资信息 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class MediaServiceImpl extends ServiceImpl<MediaMapper, Media> implements MediaService {

    @Autowired
    private RedisTemplate<String, String> strRedisTemplate;
    @Autowired
    private RedisTemplate<String, Serializable> serializableRedisTemplate;

    @Value("${aliyun.region}")
    private String region;
    @Value("${aliyun.accessKeyId}")
    private String accessKeyId;
    @Value("${aliyun.accessKeySecret}")
    private String accessKeySecret;

    /*
    业务分析：
    *   1.判断关键
    *       companyid  filename  fileid(videoId)
    *   2.给信息数据赋值默认值
    *       auditStatus：未审核
    *
    *   3.保存信息
    *   4.将数据库最新数据返回
     */
    @Override
    @Transactional
    public MediaDTO createMedia(MediaDTO mediaDTO) {
        //    *   1.判断关键
        //    *       companyid  filename  fileid(videoId)
        if (ObjectUtils.isEmpty(mediaDTO.getCompanyId()) || StringUtil.isBlank(mediaDTO.getFilename()) || StringUtil.isBlank(mediaDTO.getFileId())) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //    *   2.给信息数据赋值默认值
        //    *       auditStatus：待审核
        mediaDTO.setAuditStatus(AuditEnum.AUDIT_UNPAST_STATUS.getCode());
        //    *   3.保存信息
        Media media = EntityMapper.INSTANCE.mediaDTOToPo(mediaDTO);
        boolean save = this.save(media);
        if (!save) {
            ExceptionCast.cast(MediaErrorCode.E_140001);
        }
        //    *   4.将数据库最新数据返回
        Media po = this.getById(media.getId());
        MediaDTO dto = EntityMapper.INSTANCE.mediaToDTO(po);
        return dto;
    }

    /*
    1.判断传入的分页查询是否合法（如果是负数进行更改）
    2.调用MP分页查询API对象
    3.为分页查询设置条件（判断条件内容是否为空）
    4.调用方法根据条件查询页面数据
    5。获取当前页数据和总数量
    6.判断当前页数据是否为空，否的话使用mapstruct转换接口把po实体类的数据转换成DTO数据封装到PageVo返回给前端

     运营查询也在
     */
    @Override
    public PageVO selectMedia(PageRequestParams params, QueryMediaModel model, Long companyId) {
        // 1.判断传入的分页查询是否合法（如果是负数进行更改）
        if (params.getPageSize() < 1) {
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }
        if (params.getPageNo() < 1) {
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }
        //    2.调用MP分页查询API对象
        Page<Media> mediaPage = new Page<>(params.getPageNo(), params.getPageSize());
        //    3.为分页查询设置条件（判断条件内容是否为空）
        LambdaQueryWrapper<Media> mediaQueryWrapper = new LambdaQueryWrapper<>();
        mediaQueryWrapper.eq(Media::getStatus, TeachPlanEnum.FIRST_LEVEL.toString());
        mediaQueryWrapper.eq(StringUtil.isNotBlank(model.getType()), Media::getType, model.getType());
        mediaQueryWrapper.like(StringUtil.isNotBlank(model.getFilename()), Media::getFilename, model.getFilename());

        if (!ObjectUtils.nullSafeEquals(companyId, MediaAuditController.COMPANY)) {
            mediaQueryWrapper.eq(Media::getCompanyId, companyId);

        }
        //    4.调用方法根据条件查询页面数据
        Page<Media> page = this.page(mediaPage, mediaQueryWrapper);
        //    5。获取当前页数据和总数量
        List<Media> records = page.getRecords();
        long total = page.getTotal();

        List<MediaDTO> mediaDTOS = Collections.emptyList();
        //    6.判断当前页数据是否为空，否的话使用mapstruct转换接口把po实体类的数据转换成DTO数据封装到PageVo返回给前端
        if (!CollectionUtils.isEmpty(records)) {

            mediaDTOS = EntityMapper.INSTANCE.mediaToMediaList(records);
        }
        PageVO pageVO = new PageVO(mediaDTOS, total, params.getPageNo(), params.getPageSize());
        return pageVO;
    }

    /*
    业务分析：
 获取url地址
	1.判断关键数据
		mediaId、companyId
	2.判断业务数据
		媒资信息
			判断是否存在（mediaId、companyId）
			判断媒资类型，如果是视频的话，要获得阿里云视频点播的播放地址
	3.获得媒资资源的路径地址Url
			获得最新的播放地址，从阿里云上获得
			PS：优化--要从缓存中获得url（ps：缓存中必须存在url--1.创建media获得，2.服务端启动时来获得--定时任务）
			缓存中的url---设置缓存中的ttl
	4.返回结果内容


     */
    @Override
    public String selectMediaVideo(Long mediaId, Long companyId) {

        if (ObjectUtils.isEmpty(mediaId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        Media media = this.getById(mediaId);
        if (ObjectUtils.isEmpty(media)) {
            ExceptionCast.cast(MediaErrorCode.E_140005);
        }
        if (ObjectUtils.isEmpty(media.getCompanyId()) || ObjectUtils.isEmpty(media.getId())) {
            ExceptionCast.cast(MediaErrorCode.E_140005);
        }


        String url = "1";
        if (ResourceTypeEnum.VIDEO.getCode().equals(media.getType())) {
            url = (String) serializableRedisTemplate.opsForValue().get("aliyunVideoURl");

            if (url == null) {
                url = getVideoUrl(media.getFileId());
                serializableRedisTemplate.opsForValue().set("aliyunVideoURl", url);
            }

        }

        return url;
    }

    public String getVideoUrl(String fileId) {
        // 1.获得请求对象
        GetPlayInfoRequest request = new GetPlayInfoRequest();
        request.setVideoId(fileId);


        //2.获得客户端对象
        String playURL = null;
        try {
            DefaultAcsClient defaultAcsClient = AliyunVODUtil.initVodClient(region, accessKeyId, accessKeySecret);

            GetPlayInfoResponse response = AliyunVODUtil.getPlayInfo(defaultAcsClient, fileId);


            List<GetPlayInfoResponse.PlayInfo> playInfoList = response.getPlayInfoList();
            if (ObjectUtils.isEmpty(playInfoList)) {
                GetPlayInfoResponse.PlayInfo info = playInfoList.get(0);
            }
            //播放地址

            for (GetPlayInfoResponse.PlayInfo playInfo : playInfoList) {
                playURL = playInfo.getPlayURL();
            }
        } catch (Exception e) {
            String message = e.getMessage();
            log.error(MediaErrorCode.E_140011.getDesc() + " : {}", message);

            // throw new BusinessException();
            // 编译器的问题：只看代码，不管代码中方法调用的结果内容
            ExceptionCast.cast(CommonErrorCode.UNKOWN);
        }
        return playURL;
    }



    /*
        业务分析：
        删除媒资信息
	1.判断关键数据
		mediaId、companyId
	2.判断业务数据
		媒资信息
			判断是否存在
			判断审核状态：只有审核通过不能删除
			判断媒资信息是否有绑定课程计划数据
				绑定媒资和课程计划数据：content（发送feign）
											   teachplan_media
	3.删除媒资信息
		媒资服务中的数据：Media
		阿里云对应的视频
     */
    @Override
    public void deleteMedia(Long mediaId,Long companyId) {

        if (ObjectUtils.isEmpty(mediaId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        Media media = this.getById(mediaId);
        if (ObjectUtils.isEmpty(media)) {
            ExceptionCast.cast(MediaErrorCode.E_140005);
        }

        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(media.getAuditStatus())) {
            ExceptionCast.cast(MediaErrorCode.E_140008);
        }

        LambdaUpdateWrapper<Media> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(Media::getCompanyId, companyId)
                .eq(Media::getId, mediaId)
                .set(Media::getStatus, TeachPlanEnum.FIRST_PARENTID_FLAG.toString())
                .set(Media::getChangeDate, LocalDateTime.now());
        boolean update = this.update(wrapper);
        if (!update) {
            ExceptionCast.cast(MediaErrorCode.E_140002);
        }
    }



    /*
     *   1.判断关键数据
     *       auditMind  auditStatus  mediaid
     *
     *   2.判断业务数据
     *       媒资信息
     *           判断是否存在
     *           判断是否删除
     *           判断审核状态
     *               必须为：待审核
     *       审核状态
     *           运营平台只能给课程审核状态赋值：审核通过、审核未通过
     *   3.修改课程审核信息
     *       auditMind  auditStatus
     */

    @Override
    public void selectAuditMedia(QueryAuditMediaModel model) {

        if (StringUtil.isBlank(model.getAuditMind()) || StringUtil.isBlank(model.getAuditStatus()) || ObjectUtils.isEmpty(model.getId())) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }


        LambdaQueryWrapper<Media> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Media::getId, model.getId())
                .eq(Media::getAuditStatus, AuditEnum.AUDIT_UNPAST_STATUS.getCode())
                .eq(Media::getStatus, CommonEnum.USING_FLAG.getCode());
        int count = this.count(wrapper);
        if (count < 1) {
            ExceptionCast.cast(MediaErrorCode.E_140004);
        }


        if (CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(model.getAuditStatus())
                || CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode().equals(model.getAuditStatus())
                || CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(model.getAuditStatus()))
        {
            ExceptionCast.cast(MediaErrorCode.E_140004);
        }


        LambdaUpdateWrapper<Media> mediaUpdateWrapper = new LambdaUpdateWrapper<>();
        mediaUpdateWrapper.eq(Media::getId, model.getId())
                .set(Media::getAuditStatus, model.getAuditStatus())
                .set(Media::getAuditMind, model.getAuditMind())
                .set(Media::getChangeDate, LocalDateTime.now());
        boolean update = this.update(mediaUpdateWrapper);
        if (!update) {
            ExceptionCast.cast(MediaErrorCode.E_140009);
        }
    }


    /*
    查询媒资信息--Feign

     *   PS：由于是学成内部微服务间的调用，此功能只需要提供基础数据即可，不需要加上业务逻辑判断
    *   如果为了数据的完整性，可以对传入和传出的参数进行判断
    *
    *   1.判断mediaid是否为空
    *   2.查询数据
    *   3.判断数据是否存在
    *   4.如果存在，使用RestResponse来封装返回
     */
    @Override
    public RestResponse<MediaDTO> selectMediaId4s(Long mediaId) {

        if (ObjectUtils.isEmpty(mediaId)) {
            RestResponse.validfail(CommonErrorCode.E_100101);
        }

        Media media = this.getById(mediaId);
        if (ObjectUtils.isEmpty(media)) {
            RestResponse.validfail(MediaErrorCode.E_140005);
        }

        MediaDTO mediaDTO = EntityMapper.INSTANCE.mediaToDTO(media);

        return RestResponse.success(mediaDTO);
    }
}
